package hr.pzrinscak.activiti;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;

public class WorkflowDeployer implements InitializingBean, ApplicationContextAware {

    static Logger log = LoggerFactory.getLogger(WorkflowDeployer.class);

    private Resource[] deploymentResources;

    public void setDeploymentResources(final Resource[] resources) {
        this.deploymentResources = resources;
    }

    private String category;

    public void setCategory(final String category) {
        this.category = category;
    }

    ApplicationContext appCtx;

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        this.appCtx = applicationContext;

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (category == null) {
            throw new FatalBeanException("Missing property: category");
        }

        if (deploymentResources != null) {
            RepositoryService repositoryService = appCtx.getBean(RepositoryService.class);
            for (Resource r : deploymentResources) {

                String deploymentName = category + "_" + r.getFilename();
                String resourceName = r.getFilename();

                boolean doDeploy = true;
                List<Deployment> deployments = repositoryService.createDeploymentQuery().deploymentName(deploymentName).orderByDeploymenTime().desc().list();
                if (!deployments.isEmpty()) {
                    Deployment existing = deployments.get(0);
                    try {
                        InputStream in = repositoryService.getResourceAsStream(existing.getId(), resourceName);
                        if (in != null) {
                            doDeploy = !contentEquals(in, r.getInputStream());

//                            File tempDir = new File(System.getProperty("java.io.tmpdir"), "activiti-deployment-temp");
//                            File f = File.createTempFile("depl",".xml", tempDir);
//                            f.deleteOnExit();
//                            OutputStream out=new FileOutputStream(f);
//                            IOUtils.copy(in,out);
//                            in.close();
//                            out.close();
//
//                            doDeploy =  (FileUtils.checksumCRC32(f) != FileUtils.checksumCRC32(r.getFile()));

                        } else {
                            throw new ActivitiException("Unable to read resource " + resourceName + ", input stream is null");
                        }
                    } catch (ActivitiException ex) {
                        //do nothing, simply re-deploy
                        log.error("Unable to read " + resourceName + " of deployment " + existing.getName() + ", id: " + existing.getId() + ", will re-deploy");
                    }

                }

                if (doDeploy) {
                    repositoryService.createDeployment()
                            .name(deploymentName)
                            .addInputStream(resourceName, r.getInputStream())
                            .deploy();
                    log.warn("Deployed BPMN20 resource " + r.getFilename());
                }

            }
        }
    }

    private boolean contentEquals(InputStream input1, InputStream input2) throws IOException {
      if (!(input1 instanceof BufferedInputStream))
      {
        input1 = new BufferedInputStream(input1);
      }
      if (!(input2 instanceof BufferedInputStream))
      {
        input2 = new BufferedInputStream(input2);
      }

      int ch = input1.read();
      while (-1 != ch)
      {
        int ch2 = input2.read();
        if (ch != ch2)
        {
          return false;
        }
        ch = input1.read();
      }

      int ch2 = input2.read();
      return (ch2 == -1);
    }

}